﻿// this object pool implemetion was infulenced by Oliver Strum's blog post
// http://www.sturmnet.org/blog/archives/2005/10/18/object-pooling8/
// and the Spring.Net Object Pool implemenation http://www.springframework.net/

// Copyright (c) Kona Framework Team. All rights reserved.
// For continued development and updates, visit http://www.codeplex.com/kona
// For a copy of the license please vist: http://www.codeplex.com/kona/license

using System;
using System.Collections.Generic;
using System.Linq;

namespace Kona.Data.Core
{
    public class Pool<T> : IPool<T>
    {

        sealed private class Lane
        {
            public Lane(T poolItem)
            {
                if(Equals(poolItem, default(T)))
                {throw new ArgumentNullException("poolItem");}
                this.PoolItem = poolItem;
            }

            public T PoolItem { get; private set; }

            public bool IsInUse { get; set; }
        }

        readonly List<Lane> lanes = new List<Lane>();
        readonly object lockObject = new object();


        public Pool(IObjectFactory<T> objectFactory, int initialPoolSize)
        {
            this.ObjectFactory = objectFactory;
            if(initialPoolSize > 0)
            {
                InitializePool(initialPoolSize);
            }
        }

        public int Size
        {
            get
            {
                lock (lockObject)
                    return this.lanes.Count;
            }
        }

        public T GetObject()
        {

            lock (lockObject)
            {
                Lane openLane = FindOpenLane();
                if (openLane != null)
                {
                    openLane.IsInUse = true;
                    return openLane.PoolItem;
                }
            }

            throw new PoolSizeExceededExceptionException();
        }

        public void ReleaseObject(T poolObject)
        {
            lock (lockObject)
            {
                Lane currentLane = this.lanes.Find(lane => EqualityComparer<T>.Default.Equals(lane.PoolItem, poolObject));
                if (currentLane != null)
                {
                    currentLane.IsInUse = false;
                }
                else
                {
                    throw new ArgumentException("The given object is not part of this pool.");
                }
            }
        }

        private Lane FindOpenLane()
        {
            return (from lane in this.lanes
                    where !lane.IsInUse
                    select lane).FirstOrDefault();
        }

        private void InitializePool(int size)
        {
			if (size <= 0)
				throw new ArgumentOutOfRangeException("Size must be greater than zero.");
			lock (lockObject) {
                //size = Math.Min(this.MaxPoolSize - this.lanes.Count, size);

				for (int i = 0; i < size; i++) {
					this.lanes.Add(new Lane(this.ObjectFactory.CreateObject( )));
				}
			}
        }


        public int MaxPoolSize { get; private set; }
        public IObjectFactory<T> ObjectFactory { get; protected internal set; }
    }
}
